package cn.felord.common.security;

import cn.hutool.core.collection.CollectionUtil;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 访问策略 所有的资源都要经过鉴权    某些资源只有鉴权通过才可以访问
 *
 * @author Dax
 * @since 18 :01  2018/9/15
 */
public class CustomAccessDecisionManager implements AccessDecisionManager {
    /**
     * 超级管理员权限码
     **/
    private static final String ROLE_ADMIN_CODE = "ROLE_ADMIN";

    /**
     * Instantiates a new Custom access decision manager.
     */
    public CustomAccessDecisionManager() {
    }

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (CollectionUtil.isEmpty(configAttributes)) {
            return;
        }

//      用户的 hasRole
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        if (CollectionUtil.isEmpty(authorities)) {
            throw new AccessDeniedException("subject has no permission yet");
        }

        List<String> auths = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
//        如果是超级管理员 放行
        if (auths.contains(ROLE_ADMIN_CODE)) {
            return;
        }
        List<String> attributes = configAttributes.stream().map(ConfigAttribute::getAttribute).collect(Collectors.toList());

//      有交集 则通过
        if (CollectionUtil.containsAny(auths, attributes)) {
            return;
        }
        throw new AccessDeniedException("subject is denied");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}
